package lines.gameengine;

import lines.gameengine.context.FieldContextHolder;
import lines.gameengine.exception.LinesEngineException;
import lines.gameengine.tracer.SearchResult;
import lines.gameengine.util.IBallGenerator;
import lines.gameengine.util.ICellsChooser;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * todo UT
 * @author vpotapenko
 */
public class FieldController extends FieldContextHolder implements IFieldListener {

    private final IField field;

    public FieldController(IField field) {
        super(field.getContext());
        this.field = field;

        field.addListener(this);
    }

    public IField getField() {
        return field;
    }

    public void prepare() throws LinesEngineException {
        field.prepare();
        getScoreHolder().reset();

        doStep();
    }

    public void reset() {
        field.reset();

        getScoreHolder().reset();
    }

    public void selectPoint(FieldPoint point) throws LinesEngineException {
        if (!field.isValidPoint(point))
            throw new LinesEngineException("Point " + point + " is not valid");

        if (field.getSelectedPoint() == null) {
            doSelectPoint(point);
        } else {
            doUserStep(point);
        }
    }

    private void doUserStep(FieldPoint point) throws LinesEngineException {
        if (field.getCell(point).isEmpty()) {
            final FieldPoint startPoint = field.getSelectedPoint();
            SearchResult result = getTracer().findTrace(startPoint, point);
            if (!result.hasTrace()) return;

            field.moveBall(startPoint, point, result.getTrace());
            if (!applyBallStrategy(point)) doStep();
        } else {
            doSelectPoint(point);
        }
    }

    private boolean applyBallStrategy(FieldPoint point) {
        IBall ball = field.getCell(point).getBall();
        return ball.getStrategy().apply(point);
    }

    private void doStep() throws LinesEngineException {
        IBallGenerator ballGenerator = getBallGenerator();
        Iterator<IBall> newBallsIt = ballGenerator.generateNextStepBalls(field).iterator();

        ICellsChooser cellsChooser = getCellsChooser();
        Collection<FieldPoint> points = cellsChooser.chooseEmptyCellsForNextStep(field);

        for (FieldPoint point : points) {
            IBall ball = newBallsIt.next();
            field.addNewBall(point, ball);
        }
    }

    private void doSelectPoint(FieldPoint point) {
        if (!field.getCell(point).isEmpty())
            field.setSelectedPoint(point);
    }

    @Override
    public void newBallAdded(FieldPoint point) {
        // do nothing
    }

    @Override
    public void ballsCleared(Collection<FieldPoint> points) {
        getScoreHolder().dispatchClearBalls(points.size());
    }

    @Override
    public void ballMoved(List<FieldPoint> trace) {
        // do nothing
    }

    @Override
    public void pointSelected(FieldPoint point) {
        // do nothing
    }
}
